#pragma once
#include "../comm/Log.h"
#include "../comm/util.h"
#include "../comm/msg.pb.h"
#include <unordered_map>
#include <memory>
#include <mutex>

namespace ns_bind
{
    using namespace ns_log;
    using namespace ns_util;
    using namespace ns_proto;

    // 绑定信息描述
    struct Bind
    {
        using ptr = shared_ptr<Bind>;

        // 通过交换机名称和队列名称共同来确定一条绑定信息
        string _exchange_name;
        string _queue_name;
        string _binding_key; // 绑定信息
        bool _durable;

        Bind() {}

        Bind(const string &ename, const string &qname, const string &binding_key,
             const bool &durable)
            : _exchange_name(ename), _queue_name(qname), _binding_key(binding_key),
              _durable(durable)
        {
        }
    };

    //-----------------------------------------------------------------------------
    // 队列和交换机 与 绑定信息都是一一对应的关系
    // 而队列与交换机是多对多关系
    // 一个交换机里可以有多个队列信息，而多个交换机也可能具有同一个队列信息
    // 所以当根据交换机名称查取数据时，就可以得到该交换机的所有队列绑定信息
    using QueueBindMap = unordered_map<string, Bind::ptr>;
    using QueueBindMapPtr = shared_ptr<QueueBindMap>;       // 将队列名称和绑定信息关联
    using BindMap = unordered_map<string, QueueBindMapPtr>; // 将交换机名称和QueueBind关联
    // 从而绑定信息的内存结构类型(交换机名称和队列名称共同来确定一条绑定信息)
    //-----------------------------------------------------------------------------
    // 如果分别构建交换机和队列与绑定的映射，则删除绑定信息(shared_ptr)就需要进行遍历

    // 绑定信息持久化管理(将绑定数据存储到数据库中)
    class BindDurable
    {
    public:
        BindDurable(const string &dbfile)
            : _sql_helper(dbfile)
        {
            // 如果传递的是一个库文件路径且不存在：
            string path = FileOper::parentdir(dbfile);
            FileOper::createdir(path);

            assert(_sql_helper.open()); // 创建、打开库
            create_table();             // 创建表
        }

        // 创建、删除绑定信息数据表
        void create_table()
        {
            string CREATE_TABLE = "create table if not exists bind_table(\
            exchange_name varchar(32), queue_name varchar(32), \
            binding_key varchar(64), durable int);";
            bool ret = _sql_helper.exec(CREATE_TABLE, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "创建绑定信息数据表失败" << endl;
                abort(); // 异常退出
            }
        }
        void delete_table()
        {
            string DROP_TABLE = "drop table if exists bind_table;";
            bool ret = _sql_helper.exec(DROP_TABLE, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除绑定信息数据表失败" << endl;
                abort(); // 异常退出
            }
        }

        // 增加、删除绑定信息
        void insert_bind(const Bind::ptr &bindp)
        {
            string INSERT_SQL = "insert into bind_table values('%s', '%s', '%s', %d);";
            char buffer[1024] = {0};
            sprintf(buffer, INSERT_SQL.c_str(), bindp->_exchange_name.c_str(),
                    bindp->_queue_name.c_str(), bindp->_binding_key.c_str(),
                    bindp->_durable);
            bool ret = _sql_helper.exec(buffer, nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "增加指定交换机队列绑定信息失败" << endl;
                return;
            }
        }
        void delete_bind(const string &ename, const string &qname)
        {
            stringstream ss;
            ss << "delete from bind_table where exchange_name=";
            ss << "'" << ename << "' ";
            ss << "and queue_name=";
            ss << "'" << qname << "';";

            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除指定交换机队列绑定信息失败" << endl;
                return;
            }
        }

        // 删除交换机、队列的所有相关绑定信息
        void delete_exchange(const string &ename)
        {
            stringstream ss;
            ss << "delete from bind_table where exchange_name=";
            ss << "'" << ename << "';";
            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除指定交换机绑定信息失败" << endl;
                return;
            }
        }
        void delete_queue(const string &qname)
        {
            stringstream ss;
            ss << "delete from bind_table where queue_name=";
            ss << "'" << qname << "';";
            bool ret = _sql_helper.exec(ss.str(), nullptr, nullptr);
            if (!ret)
            {
                LOG(WARNING) << "删除指定队列绑定信息失败" << endl;
                return;
            }
        }

        // 恢复所有的绑定信息
        BindMap recovery_all() // second类型采用指针提高管理
        {
            BindMap ret;
            string SQL = "select * from bind_table;";
            _sql_helper.exec(SQL, recovery, &ret);

            return ret;
        }

    private:
        static int recovery(void *args, int col, char **row_data, char **field_name)
        {
            // 传来的输出参数进行强转
            BindMap *ret = static_cast<BindMap *>(args);

            // 绑定信息对象的数据填充
            // bindp->_exchange_name = row_data[0];
            // bindp->_queue_name = row_data[1];
            // bindp->_binding_key = row_data[2];
            Bind::ptr bindp = make_shared<Bind>(row_data[0], row_data[1], row_data[2],
                                                static_cast<bool>(atoi(row_data[3])));

            // 将结果每一个绑定对象构建好对应的映射关系然后添加进行传来的容器中
            // ??????????????????????????????????????????????????????????

            // 因为我们的sql表中的同一个交换机可能会对应着多个队列的绑定信息
            // 所以将sql表中的数据提取到内存的unordered_map结构中可能会通过交换机名称将哈希表中数据进行覆盖
            // 故先拿出哈希表中(key-交换机)对应的数据信息，然后根据队列名称进行数据添加
            // 因为一个交换机下的队列名称肯定没有相同的
            // 如果交换机存在就直接拿出来，不存在会直接创建该交换机(哈希的operator[])
            if ((*ret)[bindp->_exchange_name] == nullptr)
                (*ret)[bindp->_exchange_name] = make_shared<QueueBindMap>();
            (*ret)[bindp->_exchange_name]->insert(make_pair(bindp->_queue_name, bindp));

            // 以下方式也可行
            //  (*ret)[bindp->_exchange_name]->insert(make_pair(bindp->_queue_name,bindp));
            return SQLITE_OK;
        }

    private:
        SqliteHelper _sql_helper;
    };

    //----------------------------------------------------------------------------
    // 绑定信息管理方法(对外提供)
    class BindManage
    {
    public:
        BindManage(const string &dbfile)
            : _bind_durable(dbfile)
        {
            // 构造的时候就进行数据恢复(同步内存和库中的绑定信息)
            _binds = _bind_durable.recovery_all();
        }

        // 添加、解除绑定
        void bind(const string &ename, const string &qname,
                  const string &binding_key, const bool& durable)
        {
            unique_lock<mutex> lock(_mtx);
            // 该语句执行时：如果不存在会创建ename对象,但是second指向的shared_ptr依旧是空指针
            if (_binds.count(ename) && _binds[ename]->count(qname))
            {
                LOG(WARNING) << "绑定信息数据已经存在" << endl;
                return;
            }

            // 先构建绑定对象
            Bind::ptr newbind = make_shared<Bind>(ename, qname, binding_key, durable);
            // 绑定信息持久化(取决于交换机和队列是否都持久化)
            if (durable) // 通过参数降低耦合度，而不是根据交换机和队列类中的信息
                _bind_durable.insert_bind(newbind);

            // 所以执行到这里时，该交换机已经存在
            if (_binds[ename] == nullptr) // 为空的话要先进行构造
                _binds[ename] = make_shared<QueueBindMap>();
            _binds[ename]->insert(make_pair(qname, newbind));
        }
        void unbind(const string &ename, const string &qname)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_binds.count(ename) || !_binds[ename]->count(qname))
            {
                LOG(WARNING) << "绑定信息数据不存在" << endl;
                return;
            }
            if (_binds[ename]->operator[](qname)->_durable)
                _bind_durable.delete_bind(ename, qname);
            _binds[ename]->erase(qname);
        }

        // 删除交换机、队列绑定信息
        void delete_exchange(const string &ename)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_binds.count(ename))
            {
                LOG(WARNING) << "不存在该交换机" << endl;
                return;
            }
            _bind_durable.delete_exchange(ename);
            _binds.erase(ename);
        }
        void delete_queue(const string &qname)
        {
            unique_lock<mutex> lock(_mtx);
            _bind_durable.delete_queue(qname);
            for (auto &bindpair : _binds)
            {
                if (bindpair.second->count(qname))
                {
                    bindpair.second->erase(qname);
                }
            }
        }

        // 获取交换机绑定信息
        const QueueBindMapPtr get_exchange_bind(const string &ename)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_binds.count(ename))
            {
                LOG(WARNING) << "交换机绑定信息数据不存在" << endl;
                return QueueBindMapPtr();
            }
            return _binds[ename];
        }

        //----------------------------------------以下用于测试
        // 获取绑定信息
        const Bind::ptr get_bind(const string &ename, const string &qname)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_binds.count(ename) || !_binds[ename]->count(qname))
            {
                LOG(WARNING) << "绑定信息数据不存在" << endl;
                return Bind::ptr();
            }
            return _binds[ename]->operator[](qname);
        }
        // 具体绑定信息是否存在
        bool exist(const string &ename, const string &qname)
        {
            unique_lock<mutex> lock(_mtx);
            if (!_binds.count(ename) || !_binds[ename]->count(qname))
            {
                return false;
            }
            return true;
        }
        // 绑定信息数量
        size_t size()
        {
            unique_lock<mutex> lock(_mtx);
            int ret = 0;
            for (auto &bindpair : _binds)
            {
                ret += bindpair.second->size();
            }
            return ret;
        }
        // 绑定信息清空
        void clear()
        {
            unique_lock<mutex> lock(_mtx);
            _binds.clear();
            _bind_durable.delete_table();
        }

    private:
        BindMap _binds;
        BindDurable _bind_durable;
        mutex _mtx;
    };

}